#region Using directives
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using SharpWind.Helpers;
using SharpWind.Resources;
using SharpWind.States;
using SharpWind.Graphics;
using SharpWind.Graphics.Text;
using SharpWind.Sound;
using SharpWind.Input;
using SharpWind.Network;
using Lidgren.Library.Network;
#endregion

namespace SharpWind
{
	public class Engine : Game
	{
		#region Fields

		private static readonly Engine instance = new Engine();

		private GraphicsDeviceManager graphicsManager = null;

		private ContentManager contentManager = null;

		private Content content = null;

		private StateManager stateManager = null;

		private TextManager textManager = null;

		private InputManager inputManager = null;

		private NetworkManager networkManager = null;

		private SpriteBatch spriteBatch = null;

		private PerfomanceMonitor performanceMonitor = null;

		private GarbageManager garbageManager = null;

		private bool isActive = false;

		private Settings settings = Settings.Empty;

		private Matrix projectionMatrix = Matrix.Identity;

		private Matrix worldMatrix = Matrix.Identity;

		private Matrix viewMatrix = Matrix.Identity;

		#endregion

		#region Constructors

		private Engine()
		{
			this.graphicsManager = new GraphicsDeviceManager( this );
			this.graphicsManager.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>( OnPreparingDeviceSettings );

			ChangeSettings( Settings.Default );

			this.IsFixedTimeStep = false;

			this.contentManager = new ContentManager( this.Services );
			this.content		= new Content();

			this.stateManager	= new StateManager();

			this.inputManager	= new InputManager();

			this.networkManager = new NetworkManager();

			this.textManager	= new TextManager();

			this.garbageManager = new GarbageManager();
			this.garbageManager.Add( GraphicsDevice );
		}

		#endregion

		#region Methods 

		public void ChangeSettings( Settings settings )
		{
			if( this.settings != settings )
			{
				this.settings = settings;
				this.Window.Title = this.settings.WindowTitle;

				this.graphicsManager.MinimumVertexShaderProfile		= ShaderProfile.VS_2_0;
				this.graphicsManager.MinimumPixelShaderProfile		= ShaderProfile.PS_2_0;
				this.graphicsManager.PreferredBackBufferWidth		= settings.ResolutionWidth;
				this.graphicsManager.PreferredBackBufferHeight		= settings.ResolutionHeight;
				this.graphicsManager.SynchronizeWithVerticalRetrace = false;
				this.graphicsManager.IsFullScreen					= settings.FullScreen;

				this.graphicsManager.ApplyChanges();
			}
		}

		public void SetAlphaBlendingEnabled( bool value )
		{
			if( value )
			{
				GraphicsDevice.RenderState.AlphaBlendEnable = true;
				GraphicsDevice.RenderState.SourceBlend		= Blend.SourceAlpha;
				GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
			}
			else
			{
				GraphicsDevice.RenderState.AlphaBlendEnable = false;
			}
		}

		public void SetCurrentAlphaMode( AlphaMode mode )
		{
			switch( mode )
			{
				case AlphaMode.Default:
					GraphicsDevice.RenderState.SourceBlend		= Blend.SourceAlpha;
					GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
					break;

				case AlphaMode.Disable:
					GraphicsDevice.RenderState.SourceBlend		= Blend.Zero;
					GraphicsDevice.RenderState.DestinationBlend = Blend.One;
					break;

				case AlphaMode.OneOne:
					GraphicsDevice.RenderState.SourceBlend		= Blend.One;
					GraphicsDevice.RenderState.DestinationBlend = Blend.One;
					break;

				case AlphaMode.SourceAlphaOne:
					GraphicsDevice.RenderState.SourceBlend		= Blend.SourceAlpha;
					GraphicsDevice.RenderState.DestinationBlend = Blend.One;
					break;
			}
		}

		public void ToggleFullScreen()
		{
			this.settings.FullScreen = !this.settings.FullScreen;
			this.graphicsManager.ToggleFullScreen();
		}

		protected override void Initialize()
		{
			this.performanceMonitor = new PerfomanceMonitor();
			
			base.Initialize();
			this.graphicsManager.DeviceReset += new EventHandler( OnDeviceReset );
			OnDeviceReset( null, EventArgs.Empty );
		}

		protected override void Update( GameTime gameTime )
		{
			this.networkManager.Update( gameTime );

			if( this.isActive )
			{
				this.performanceMonitor.Update( gameTime );

				this.inputManager.Update();
				//this.content.Sounds.Update();

				if( this.stateManager.CurrentState != null )
				{
					this.stateManager.CurrentState.Update( gameTime );
				}
			}

			base.Update( gameTime );
		}

		protected override void Draw( GameTime gameTime )
		{
			if( this.isActive )
			{
				ClearScreen();

				if( this.stateManager.CurrentState != null )
				{
					this.stateManager.CurrentState.Draw( gameTime );
				}

				PerformanceMonitor.Draw( gameTime );

				Texts.Draw();
			}

			base.Draw( gameTime );
		}

		protected override void OnActivated( object sender, EventArgs args )
		{
			this.isActive = true;
			base.OnActivated( sender, args );
		}

		protected override void OnDeactivated( object sender, EventArgs args )
		{
			this.isActive = false;
			base.OnDeactivated( sender, args );
		}

		protected override void LoadGraphicsContent( bool loadAllContent )
		{
			if( loadAllContent )
			{
				this.content.LoadContent( this.contentManager );
				this.spriteBatch = new SpriteBatch( GraphicsDevice );
			}

			base.LoadGraphicsContent( loadAllContent );
		}

		protected override void UnloadGraphicsContent( bool unloadAllContent )
		{
			if( unloadAllContent )
			{
				this.contentManager.Unload();
			}

			base.UnloadGraphicsContent( unloadAllContent );
		}

		protected override void Dispose( bool disposing )
		{
			try
			{
				if( this.networkManager.Status == NetConnectionStatus.Connected ||
					this.networkManager.Status == NetConnectionStatus.Connecting )
				{
					this.networkManager.Disconnect();
				}

				this.contentManager.Unload();
				this.garbageManager.Dispose();
			}
			finally
			{
				base.Dispose( disposing );
			}
		}

		private void ClearScreen()
		{
			GraphicsDevice.Clear( ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0 );
		}

		private void OnPreparingDeviceSettings( object sender, PreparingDeviceSettingsEventArgs e )
		{
			PresentationParameters presentParams = new PresentationParameters();
			presentParams.MultiSampleType		 = MultiSampleType.FourSamples;
			presentParams.PresentationInterval	 = PresentInterval.One;
		}

		private void OnDeviceReset( object sender, EventArgs e )
		{
			int width	 = GraphicsDevice.Viewport.Width;
			int height	 = GraphicsDevice.Viewport.Height;
			float aspect = (float)width / (float)height;

			this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, aspect, 0.1f, 1000f );

			GraphicsDevice.RenderState.DepthBufferEnable		= true;
			GraphicsDevice.RenderState.DepthBufferWriteEnable	= true;

			GraphicsDevice.SamplerStates[0].AddressU			= TextureAddressMode.Wrap;
			GraphicsDevice.SamplerStates[0].AddressV			= TextureAddressMode.Wrap;
			GraphicsDevice.SamplerStates[0].MagFilter			= TextureFilter.Anisotropic;
			GraphicsDevice.SamplerStates[0].MinFilter			= TextureFilter.Anisotropic;
			GraphicsDevice.SamplerStates[0].MipFilter			= TextureFilter.Anisotropic;

			SetCurrentAlphaMode( AlphaMode.Default );
		}

		#endregion

		#region Properties

		public static Engine Instance
		{
			get
			{
				return instance;
			}
		}

		public Settings Settings
		{
			get
			{
				return this.settings;
			}
			set
			{
				if( this.settings != value )
				{
					ChangeSettings( value );
				}
			}
		}

		public Content Content
		{
			get
			{
				return this.content;
			}
		}

		public InputManager Input
		{
			get
			{
				return this.inputManager;
			}
		}

		public NetworkManager Network
		{
			get
			{
				return this.networkManager;
			}
		}

		public TextManager Texts
		{
			get
			{
				return this.textManager;
			}
		}

		public StateManager States
		{
			get
			{
				return this.stateManager;
			}
		}

		public PerfomanceMonitor PerformanceMonitor
		{
			get
			{
				return this.performanceMonitor;
			}
		}

		public Matrix WorldMatrix
		{
			get
			{
				return this.worldMatrix;
			}
			set
			{
				this.worldMatrix = value;
			}
		}

		public Matrix ViewMatrix
		{
			get
			{
				return this.viewMatrix;
			}
			set
			{
				this.viewMatrix = value;
			}
		}

		public Matrix ProjectionMatrix
		{
			get
			{
				return this.projectionMatrix;
			}
			set
			{
				this.projectionMatrix = value;
			}
		}

		public Matrix ViewProjectionMatrix
		{
			get
			{
				return this.viewMatrix * this.projectionMatrix;
			}
		}

		public Matrix WorlViewProjectionMatrix
		{
			get
			{
				return this.worldMatrix * this.viewMatrix * this.projectionMatrix;
			}
		}

		internal GraphicsDevice GraphicsDevice
		{
			get
			{
				return this.graphicsManager.GraphicsDevice;
			}
		}

		internal SpriteBatch SpriteBatch
		{
			get
			{
				return this.spriteBatch;
			}
		}

		internal GarbageManager Garbage
		{
			get
			{
				return this.garbageManager;
			}
		}

		#endregion
	}
}
